package org.java.base.channel;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.Future;

/**
 * 在Java 7中，异步filechannel被添加到Java NIO中。AsynchronousFileChannel使数据异步读取和写入文件成为可能。
 */
public class AsynchronousFileChannelDemo {

    public static void main(String[] args) {

    }

    /**
     * 异步读数据
     *
     * @param path
     * @throws IOException
     */
    public static void asynchronizRead(Path path) throws IOException {
        AsynchronousFileChannel fileChannel =
            AsynchronousFileChannel.open(path, StandardOpenOption.READ);

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        long position = 0;

        Future<Integer> operation = fileChannel.read(buffer, position);

        while (!operation.isDone()) ;

        buffer.flip();
        byte[] data = new byte[buffer.limit()];
        buffer.get(data);
        System.out.println(new String(data));
        buffer.clear();
    }

    /**
     * 异步读数据第二种方法：
     * 从AsynchronousFileChannel读取数据的第二种方法是调用read()方法版本，该方法以CompletionHandler为参数。下面是如何调用read()方法:
     */
    public static void secondAsynchronizRead(Path path) throws IOException {
        AsynchronousFileChannel fileChannel =
            AsynchronousFileChannel.open(path, StandardOpenOption.READ);
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        long position = 0;
        fileChannel.read(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {
            @Override
            public void completed(Integer result, ByteBuffer attachment) {
                System.out.println("result = " + result);

                attachment.flip();
                byte[] data = new byte[attachment.limit()];
                attachment.get(data);
                System.out.println(new String(data));
                attachment.clear();
            }

            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {

            }
        });
    }

    /**
     * 异步写数据
     * <p>
     * 与读取一样，可以通过两种方式将数据写入AsynchronousFileChannel。
     * 每一种写入数据的方法都调用AsynchronousFileChannel的write()方法之一。
     */
    public static void asyncronizedWrite() throws IOException {
        Path path = Paths.get("data/test-write.txt");
        AsynchronousFileChannel fileChannel =
            AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        long position = 0;

        buffer.put("test data".getBytes());
        buffer.flip();

        Future<Integer> operation = fileChannel.write(buffer, position);
        buffer.clear();

        while (!operation.isDone()) ;

        System.out.println("Write done");
    }

    /**
     * 异步写数据 第二种方法
     * 你也可以用CompletionHandler来告诉异步filechannel写数据何时完成，而不是用Future。
     * 下面是一个使用CompletionHandler写入数据到AsynchronousFileChannel的例子:
     */
    public static void secondAsyncronizedWrite() throws IOException {

        Path path = Paths.get("data/test-write.txt");
        if (!Files.exists(path)) {
            Files.createFile(path);
        }
        AsynchronousFileChannel fileChannel =
            AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        long position = 0;

        buffer.put("test data".getBytes());
        buffer.flip();

        fileChannel.write(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {

            @Override
            public void completed(Integer result, ByteBuffer attachment) {
                System.out.println("bytes written: " + result);
            }

            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {
                System.out.println("Write failed");
                exc.printStackTrace();
            }
        });
    }
}
